/*
 * LDIR
 *
 * LDIR, a GPS cartography application built for Let's Do It Romania eco
 * campaign.
 * Copyright (C) 2010 Genera Software <office@generasoftware.com>
 *
 * http://generasoftware.com/
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option) any later
 * version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
 * Place, Suite 330, Boston, MA 02111-1307 USA
 */
//  Include Files
#include "Json.h"	// CJson
#include "JsonImpl.h"
#include "Json.pan"	  	// panic codes
#include <reent.h>
_LIT8(KIndent, "    ");

//  Member Functions

EXPORT_C CJsonParser* CJsonParser::NewLC( const TStringTable& aDictionary,
    TBool aAllowComments/* = EFalse*/, TBool aCheckUTF8/* = ETrue*/)
    {
    CJsonParser* self = new ( ELeave ) CJsonParser( aDictionary );
    CleanupStack::PushL( self );
    self->ConstructL( aAllowComments, aCheckUTF8 );
    return self;
    }

EXPORT_C CJsonParser* CJsonParser::NewL( const TStringTable& aDictionary,
    TBool aAllowComments/* = EFalse*/, TBool aCheckUTF8/* = ETrue*/)
    {
    CJsonParser* self = CJsonParser::NewLC( aDictionary, aAllowComments,
        aCheckUTF8 );
    CleanupStack::Pop( self );
    return self;
    }

CJsonParser::CJsonParser( const TStringTable& aDictionary )
// note, CBase initializes all member variables to zero
:
    iDictionaryTable( aDictionary )
    {
    }

void CJsonParser::ConstructL( TBool aAllowComments, TBool aCheckUTF8 )
    {
    // second phase constructor, anything that may leave must be constructed here
    iParserImpl = new ( ELeave ) CJsonParserImpl( aAllowComments, aCheckUTF8 );
    iParserImpl->SetContentHandlerL( this );
    iDictionary.OpenL( iDictionaryTable );
    }

EXPORT_C CJsonParser::~CJsonParser()
    {
    delete iParserImpl;
    iDictionary.Close();
    delete iDom;
    }

EXPORT_C TVersion CJsonParser::Version() const
    {
    return iParserImpl->Version();
    }

EXPORT_C void CJsonParser::SetContentHandlerL(
    MJsonContentHandler* aContenthandler )
    {
    iParserImpl->SetContentHandlerL( aContenthandler );
    }

////////////////////////////////////////////////////////////////////////////////
EXPORT_C void CJsonParser::PrepareParser()
    {
    ResetData();
    }

EXPORT_C TInt CJsonParser::ParseL( const TDesC8& aData )
    {
    return iParserImpl->ParseL( aData );
    }

EXPORT_C void CJsonParser::ResetData()
    {
    delete iDom;
    iDom = NULL;
    iCurrentNode = NULL;
    iParserImpl->SetContentHandlerL( this );
    }

EXPORT_C void CJsonParser::OnBooleanL( TBool aBool )
    {
    CEntity* node = CEntity::NewLC( bool_type );
    node->SetValueL( ( bool ) aBool );

    if ( !iCurrentNode )
        {
        iCurrentNode = node;
        iDom = iCurrentNode;
        }

    if ( iCurrentNode->Type() == pair_type )
        {
        iCurrentNode->GetPair()->SetValue( node );
        iCurrentNode = iCurrentNode->Parent();
        }
    else if ( iCurrentNode->Type() == array_type )
        iCurrentNode->AddChildL( node );

    CleanupStack::Pop( node );
    }

EXPORT_C void CJsonParser::OnDesL( const TDesC8& aDesc )
    {
    CEntity* node = CEntity::NewLC( str_type );
    node->SetValueL( aDesc );

    if ( !iCurrentNode )
        {
        iCurrentNode = node;
        iDom = iCurrentNode;
        }

    if ( iCurrentNode->Type() == pair_type )
        {
        iCurrentNode->GetPair()->SetValue( node );
        iCurrentNode = iCurrentNode->Parent();
        }
    else if ( iCurrentNode->Type() == array_type )
        iCurrentNode->AddChildL( node );

    CleanupStack::Pop( node );
    }

EXPORT_C void CJsonParser::OnEndArrayL()
    {
    __ASSERT_DEBUG( iCurrentNode, User::Invariant());
    if ( iCurrentNode->Parent() )
        {
        iCurrentNode = iCurrentNode->Parent();
        if ( iCurrentNode->Type() == pair_type )
            iCurrentNode = iCurrentNode->Parent();
        }
    else
        iDom = iCurrentNode;
    }

EXPORT_C void CJsonParser::OnEndMapL()
    {
    __ASSERT_DEBUG( iCurrentNode, User::Invariant());
    if ( iCurrentNode->Parent() )
        {
        iCurrentNode = iCurrentNode->Parent();
        if ( iCurrentNode->Type() == pair_type )
            iCurrentNode = iCurrentNode->Parent();
        }
    else
        iDom = iCurrentNode;
    }

EXPORT_C void CJsonParser::OnErrorL( TInt /*aErrCode*/, const TDesC8& aErrDesc )
    {
    TBuf8<KMaxFullName> err;
    err = aErrDesc;
    }

EXPORT_C void CJsonParser::OnIntegerL( TInt aInt )
    {
    __ASSERT_DEBUG( iCurrentNode, User::Invariant());

    CEntity* node = CEntity::NewLC( int_type );
    node->SetValueL( aInt );

    if ( iCurrentNode->Type() == pair_type )
        {
        iCurrentNode->GetPair()->SetValue( node );
        iCurrentNode = iCurrentNode->Parent();
        }
    else if ( iCurrentNode->Type() == array_type )
        iCurrentNode->AddChildL( node );

    CleanupStack::Pop( node );
    }

EXPORT_C void CJsonParser::OnMapKeyL( const TDesC8& aKey )
    {
    __ASSERT_DEBUG( (iCurrentNode && (iCurrentNode->Type() == obj_type || iCurrentNode->Type() == array_type)),
        User::Invariant());
    CEntity* node = CEntity::NewLC( pair_type );
    CPair* pair = CPair::NewL();
    CleanupStack::PushL( pair );
    RString strval = iDictionary.OpenStringL( aKey );
    CleanupClosePushL( strval );
    const TStringTable* originalTable = strval.OriginalTableRef();
    if ( !originalTable )
        User::Panic( _L("CJsonDictionary"), KErrNotFound );
    pair->SetKeyL( strval.Index( *originalTable ) );
    node->SetValueL( pair );
    CleanupStack::PopAndDestroy( &strval );
    CleanupStack::Pop( pair );
    iCurrentNode->AddChildL( node );
    iCurrentNode = node;
    CleanupStack::Pop( node );
    }

EXPORT_C void CJsonParser::OnNullL()
    {
    __ASSERT_DEBUG( iCurrentNode, User::Invariant());

    CEntity* node = CEntity::NewLC( null_type );

    if ( iCurrentNode->Type() == pair_type )
        {
        iCurrentNode->GetPair()->SetValue( node );
        iCurrentNode = iCurrentNode->Parent();
        }
    else if ( iCurrentNode->Type() == array_type )
        iCurrentNode->AddChildL( node );

    CleanupStack::Pop( node );
    }

EXPORT_C void CJsonParser::OnNumberL( const TDesC8& aNumber )
    {
    __ASSERT_DEBUG( iCurrentNode, User::Invariant());

    CEntity* node = CEntity::NewLC( str_type );
    node->SetValueL( aNumber );

    if ( iCurrentNode->Type() == pair_type )
        {
        iCurrentNode->GetPair()->SetValue( node );
        iCurrentNode = iCurrentNode->Parent();
        }
    else if ( iCurrentNode->Type() == array_type )
        iCurrentNode->AddChildL( node );

    CleanupStack::Pop( node );
    }

EXPORT_C void CJsonParser::OnRealL( TReal aReal )
    {
    __ASSERT_DEBUG( iCurrentNode, User::Invariant());
    CEntity* node = CEntity::NewLC( real_type );
    node->SetValueL( aReal );

    if ( iCurrentNode->Type() == pair_type )
        {
        iCurrentNode->GetPair()->SetValue( node );
        iCurrentNode = iCurrentNode->Parent();
        }
    else if ( iCurrentNode->Type() == array_type )
        iCurrentNode->AddChildL( node );

    CleanupStack::Pop( node );
    }

EXPORT_C void CJsonParser::OnStartArrayL()
    {
    CEntity* node = CEntity::NewLC( array_type );
    if ( iCurrentNode )
        {
        if ( iCurrentNode->Type() == pair_type )
            {
            iCurrentNode->GetPair()->SetValue( node );
            node->SetParent( iCurrentNode );
            }
        else
            iCurrentNode->AddChildL( node );
        }

    CleanupStack::Pop( node );
    iCurrentNode = node;
    }

EXPORT_C void CJsonParser::OnStartMapL()
    {
    CEntity* node = CEntity::NewLC( obj_type );
    if ( iCurrentNode )
        {
        if ( iCurrentNode->Type() == pair_type )
            {
            iCurrentNode->GetPair()->SetValue( node );
            node->SetParent( iCurrentNode );
            }
        else
            iCurrentNode->AddChildL( node );
        }

    CleanupStack::Pop( node );
    iCurrentNode = node;
    }

EXPORT_C CEntity* CJsonParser::DOM()
    {
    return iDom;
    }

////////////////////////////////////////////////////////////////////////////////
EXPORT_C CEntity* CJsonParser::GetDOM()
    {
    CEntity* dom = iDom;
    iDom = NULL;
    return dom;
    }

// CJsonParserImpl

CJsonParserImpl::CJsonParserImpl( TBool aAllowComments, TBool aCheckUTF8 )
// note, CBase initializes all member variables to zero
    {
    iCallbacks.yajl_boolean = yajl_boolean;
    iCallbacks.yajl_double = yajl_double;
    iCallbacks.yajl_end_array = yajl_end_array;
    iCallbacks.yajl_end_map = yajl_end_map;
    iCallbacks.yajl_integer = yajl_integer;
    iCallbacks.yajl_map_key = yajl_map_key;
    iCallbacks.yajl_null = yajl_null;
    iCallbacks.yajl_number = NULL;
    iCallbacks.yajl_start_array = yajl_start_array;
    iCallbacks.yajl_start_map = yajl_start_map;
    iCallbacks.yajl_string = yajl_string;
    iConfig.allowComments = aAllowComments;
    iConfig.checkUTF8 = aCheckUTF8;
    }

CJsonParserImpl::~CJsonParserImpl()
    {
    yajl_free( iParserHandle );
    }

TVersion CJsonParserImpl::Version() const
    {
    // Version number of example API
    const TInt KMajor = 1;
    const TInt KMinor = 0;
    const TInt KBuild = 1;
    return TVersion( KMajor, KMinor, KBuild );
    }

void CJsonParserImpl::SetContentHandlerL( MJsonContentHandler* aContenthandler )
    {
    iContentHandler = aContenthandler;
    yajl_free( iParserHandle );
    iParserHandle = yajl_alloc( &iCallbacks, &iConfig, iContentHandler );
    if ( !iParserHandle )
        User::LeaveNoMemory();
    }

TInt CJsonParserImpl::ParseL( const TDesC8& aData )
    {
    yajl_status stat;
    stat = yajl_parse( iParserHandle, aData.Ptr(), aData.Length() );
    if ( stat != yajl_status_insufficient_data && stat != yajl_status_ok )
        {
        unsigned char * str = yajl_get_error( iParserHandle, 1, aData.Ptr(),
            aData.Length() );
        iContentHandler->OnErrorL( KErrGeneral, TPtrC8( str ) );
        yajl_free_error( str );
        }
    return stat;
    }

int CJsonParserImpl::yajl_null( void* ctx )
    {
    static_cast<MJsonContentHandler*> ( ctx )->OnNullL();
    return 1;
    }

int CJsonParserImpl::yajl_boolean( void * ctx, int boolVal )
    {
    static_cast<MJsonContentHandler*> ( ctx )->OnBooleanL( boolVal );
    return 1;
    }

int CJsonParserImpl::yajl_double( void * ctx, double doubleVal )
    {
    static_cast<MJsonContentHandler*> ( ctx )->OnRealL( doubleVal );
    return 1;
    }

int CJsonParserImpl::yajl_end_array( void * ctx )
    {
    static_cast<MJsonContentHandler*> ( ctx )->OnEndArrayL();
    return 1;
    }

int CJsonParserImpl::yajl_end_map( void * ctx )
    {
    static_cast<MJsonContentHandler*> ( ctx )->OnEndMapL();
    return 1;
    }

int CJsonParserImpl::yajl_integer( void * ctx, long integerVal )
    {
    static_cast<MJsonContentHandler*> ( ctx )->OnIntegerL( integerVal );
    return 1;
    }

int CJsonParserImpl::yajl_map_key( void * ctx, const unsigned char * key,
    unsigned int stringLen )
    {
    static_cast<MJsonContentHandler*> ( ctx )->OnMapKeyL( TPtrC8( key,
        stringLen ) );
    return 1;
    }

int CJsonParserImpl::yajl_number( void * ctx, const char * numberVal,
    unsigned int numberLen )
    {
    static_cast<MJsonContentHandler*> ( ctx )->OnNumberL( TPtrC8(
        ( const TUint8* ) numberVal, numberLen ) );
    return 1;
    }

int CJsonParserImpl::yajl_start_array( void * ctx )
    {
    static_cast<MJsonContentHandler*> ( ctx )->OnStartArrayL();
    return 1;
    }

int CJsonParserImpl::yajl_start_map( void * ctx )
    {
    static_cast<MJsonContentHandler*> ( ctx )->OnStartMapL();
    return 1;
    }

int CJsonParserImpl::yajl_string( void * ctx, const unsigned char * stringVal,
    unsigned int stringLen )
    {
    static_cast<MJsonContentHandler*> ( ctx )->OnDesL( TPtrC8( stringVal,
        stringLen ) );
    return 1;
    }

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//							CJsonGenerator & Impl
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

//  Member Functions

EXPORT_C CJsonGenerator* CJsonGenerator::NewLC(
    const TStringTable& aDictionary, TBool aBeautify /* = ETrue*/)
    {
    CJsonGenerator* self = new ( ELeave ) CJsonGenerator();
    CleanupStack::PushL( self );
    self->ConstructL( aDictionary, aBeautify );
    return self;
    }

EXPORT_C CJsonGenerator* CJsonGenerator::NewL( const TStringTable& aDictionary,
    TBool aBeautify /* = ETrue*/)
    {
    CJsonGenerator* self = CJsonGenerator::NewLC( aDictionary, aBeautify );
    CleanupStack::Pop( self );
    return self;
    }

CJsonGenerator::CJsonGenerator()
// note, CBase initializes all member variables to zero
    {
    }

void CJsonGenerator::ConstructL( const TStringTable& aDictionary,
    TBool aBeautify )
    {
    // second phase constructor, anything that may leave must be constructed here
    iGenerator = new ( ELeave ) CJsonGeneratorImpl( aDictionary, aBeautify,
        KIndent );
    iGenerator->ConstructL();
    }

EXPORT_C CJsonGenerator::~CJsonGenerator()
    {
    delete iGenerator;
    if ( iOwnsEntity )
        delete iRoot;
    CloseSTDLIB();
    }

EXPORT_C TVersion CJsonGenerator::Version() const
    {
    return iGenerator->Version();
    }

EXPORT_C void CJsonGenerator::SetRoot( CEntity* aRoot, TBool aOwnsEntity )
    {
    if ( iOwnsEntity && iRoot )
        {
        delete iRoot;
        }
    iOwnsEntity = aOwnsEntity;
    iRoot = aRoot;
    }

EXPORT_C void CJsonGenerator::AddChildL( CEntity* aNode )
    {
    iRoot->AddChildL( aNode );
    }

EXPORT_C TInt CJsonGenerator::GenerateL( RWriteStream& aStream )
    {
    const TUint8* buffer = NULL;
    TUint length = 0;
    TInt status = iGenerator->GenerateL( iRoot, &buffer, &length );
    if ( status == yajl_gen_generation_complete )
        aStream.WriteL( buffer, length );
    iGenerator->Free();
    return status;
    }

EXPORT_C TInt CJsonGenerator::GenerateL( HBufC8*& aBuffer )
    {
    aBuffer = NULL;
    const TUint8* buffer = NULL;
    TUint length = 0;
    TInt status = iGenerator->GenerateL( iRoot, &buffer, &length );
    if ( status == yajl_gen_generation_complete )
        {
        aBuffer = HBufC8::NewL( length );
        aBuffer->Des().Copy( buffer, length );
        }
    iGenerator->Free();
    return status;
    }

/////////////////////////////////CJsonGeneratorImpl/////////////////////////////////////////

CJsonGeneratorImpl::CJsonGeneratorImpl( const TStringTable& aDictionary,
    TBool aBeautify, const TDesC8& aIndent ) :
    iDictionaryTable( aDictionary )
    {
    iConfig.beautify = aBeautify;
    iIndent = aIndent;
    iConfig.indentString = ( const char* ) iIndent.PtrZ();
    }

void CJsonGeneratorImpl::ConstructL()
    {
    iGenHandle = yajl_gen_alloc( &iConfig );
    if ( !iGenHandle )
        User::LeaveNoMemory();
    iDictionary.OpenL( iDictionaryTable );
    }

CJsonGeneratorImpl::~CJsonGeneratorImpl()
    {
    Free();
    iDictionary.Close();
    }

TVersion CJsonGeneratorImpl::Version() const
    {
    // Version number of example API
    const TInt KMajor = 1;
    const TInt KMinor = 0;
    const TInt KBuild = 1;
    return TVersion( KMajor, KMinor, KBuild );
    }

void CJsonGeneratorImpl::Free()
    {
    if ( iGenHandle )
        {
        yajl_gen_free( iGenHandle );
        iGenHandle = NULL;
        }
    }

TInt CJsonGeneratorImpl::GenerateL( CEntity* iNode, const unsigned char ** buf,
    unsigned int * len )
    {
    Free();
    ConstructL();
    //if (!iNode->IsRootNode())
    //User::Leave(KErrArgument);
    TInt status = Generate( iNode );
    if ( status == yajl_gen_status_ok )
        status = yajl_gen_generation_complete;
    yajl_gen_get_buf( iGenHandle, buf, len );

    return status;
    }

TInt CJsonGeneratorImpl::Generate( CEntity* iNode )
    {
    TInt status = yajl_gen_status_ok;
    switch ( iNode->Type() )
        {
        case obj_type:
            {
            status = yajl_gen_map_open( iGenHandle );
            status = GenerateChildren( iNode );
            status = yajl_gen_map_close( iGenHandle );
            }
            break;
        case array_type:
            {
            status = yajl_gen_array_open( iGenHandle );
            status = GenerateChildren( iNode );
            status = yajl_gen_array_close( iGenHandle );
            }
            break;
        case null_type:
            status = yajl_gen_null( iGenHandle );
            break;
        case str_type:
            status = yajl_gen_string( iGenHandle, iNode->GetStr().Ptr(),
                iNode->GetStr().Length() );
            break;
        case int_type:
            status = yajl_gen_integer( iGenHandle, iNode->GetInt() );
            break;
        case real_type:
            status = yajl_gen_double( iGenHandle, iNode->GetReal() );
            break;
        case pair_type:
            {
            RString stringVal = iDictionary.String(
                iNode->GetPair()->iFieldIndex, iDictionaryTable );
            status = yajl_gen_string( iGenHandle, stringVal.DesC().Ptr(),
                stringVal.DesC().Length() );
            stringVal.Close();
            status = Generate( iNode->GetPair()->iValue );
            }
            break;
        case bool_type:
            status = yajl_gen_bool( iGenHandle, iNode->GetBool() );
            break;
        default:
            break;
        }
    return status;
    }

TInt CJsonGeneratorImpl::GenerateChildren( CEntity* iNode )
    {
    TInt status = yajl_gen_status_ok;
    Entities* nodeChildren = iNode->GetArray();
    if ( nodeChildren && nodeChildren->Count() )
        for ( TInt i = 0; i < nodeChildren->Count(); ++i )
            if ( ( status = Generate( ( *nodeChildren )[i] ) )
                != yajl_gen_status_ok )
                return status;

    return status;
    }
// End of File
